﻿// Fill out your copyright notice in the Description page of Project Settings.


#include "AbilitySystem/StarrySkyAbilitySystemComponent.h"

#include "StarrySkyGameplayTags.h"
#include "AbilitySystem/Abilities/StarrySkyGameplayAbility.h"


void UStarrySkyAbilitySystemComponent::AbilityActorInfoSet()
{
	//绑定游戏效果发生变化的方法,每当GE应用于自身时，都会在服务器上调用。这包括即时和基于持续时间的GE
	OnGameplayEffectAppliedDelegateToSelf.AddUObject(this, &ThisClass::ClientEffectApplied);
}

void UStarrySkyAbilitySystemComponent::AddCharacterAbilities(
	const TArray<TSubclassOf<UGameplayAbility>> StartupAbilities)
{
	//循环处理所有的启动能力
	for(const TSubclassOf<UGameplayAbility> AbilityClass : StartupAbilities)
	{
		FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(AbilityClass, 1);
		if(const UStarrySkyGameplayAbility* StarrySkyAbility = Cast<UStarrySkyGameplayAbility>(AbilitySpec.Ability))
		{
			//将能力的启动输入标签添加到能力规范的动态标签中
			AbilitySpec.GetDynamicSpecSourceTags().AddTag(StarrySkyAbility->StartupInputTag);
			//初始赋予的能力给与能力状态为装备状态
			//AbilitySpec.GetDynamicSpecSourceTags().AddTag(FStarrySkyGameplayTags::Get().Abilities_Status_Equipped);
			GiveAbility(AbilitySpec);//赋予能力但是不激活
		}
		// GiveAbilityAndActivateOnce(AbilitySpec); 
	}
}

void UStarrySkyAbilitySystemComponent::AbilityInputTagPressed(const FGameplayTag& InputTag)
{
	if(!InputTag.IsValid()) return;
	//搜索全部可激活的能力是要添加锁
	FScopedAbilityListLock ActiveScopeLock(*this);
	//获取所有可以激活的能力，并且判断是否包含输入标签
	for(FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
	{
		//判断能力是否包含着标签
		if(AbilitySpec.GetDynamicSpecSourceTags().HasTagExact(InputTag))
		{
			//能力系统输入按下的标志，其中有Spec.Ability->InputPressed未实现的虚方法可以提供我们自己的实现逻辑
			AbilitySpecInputPressed(AbilitySpec);
			//判断这个能力是否已经激活，如果激活就调用复制事件接口
			/**
			 * AbilitySpec.ActivationInfo.GetActivationPredictionKey() 中的ActivationInfoApi已经废弃
			 **/
			if(AbilitySpec.IsActive())
			{
				//使用安全方式获取预测键的更安全方式
				FPredictionKey PredictionKey;
				//需要获取实例化能力的预测键
				if (UGameplayAbility* AbilityInstance = AbilitySpec.GetPrimaryInstance())
				{
					// 对于实例化能力 - 使用新的 API
					PredictionKey = AbilityInstance->GetCurrentActivationInfo().GetActivationPredictionKey();
				}
				//用于处理网络复制事件，将本地发生的输入事件同步到服务器和其他客户端
				//加入这个函数蓝图中能力系统可以使等待按下的消息生效，不然等单输入的消息不会生效
				InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, AbilitySpec.Handle, PredictionKey);
			}
		}
	}
}

void UStarrySkyAbilitySystemComponent::AbilityInputTagHeld(const FGameplayTag& InputTag)
{
	if(!InputTag.IsValid()) return;
	FScopedAbilityListLock ActiveScopeLock(*this);
	//获取所有可以激活的能力，并且判断是否包含输入标签
	for(FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
	{
		//判断能力是否包含着标签
		if(AbilitySpec.GetDynamicSpecSourceTags().HasTagExact(InputTag))
		{
			//能力系统输入按下的标志，其中有Spec.Ability->InputPressed未实现的虚方法可以提供我们自己的实现逻辑
			AbilitySpecInputPressed(AbilitySpec);
			//判断这个能力是否已经激活
			if(!AbilitySpec.IsActive())
			{
				TryActivateAbility(AbilitySpec.Handle);//尝试激活一个能力,可能会被其他标签阻止
			}
		}
	}
}

void UStarrySkyAbilitySystemComponent::AbilityInputTagReleased(const FGameplayTag& InputTag)
{
	if(!InputTag.IsValid()) return;
	FScopedAbilityListLock ActiveScopeLock(*this);
	//获取所有可以激活的能力，并且判断是否包含输入标签
	for(FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
	{
		//判断能力是否包含着标签,已经激活的能力
		if(AbilitySpec.GetDynamicSpecSourceTags().HasTagExact(InputTag)&&AbilitySpec.IsActive())
		{
			//能力系统输入按下的标志，其中有Spec.Ability->InputPressed未实现的虚方法可以提供我们自己的实现逻辑
			AbilitySpecInputReleased(AbilitySpec);
			/**
			 *调用复制事件,复制这次事件干了什么给服务器
			 * 参数1：事件类型
			 * 参数2：能力规范句柄
			 * 参数3：预测键，上次激活的预测键
			 */
			//使用安全方式获取预测键的更安全方式
			FPredictionKey PredictionKey;
			//需要获取实例化能力的预测键
			if (UGameplayAbility* AbilityInstance = AbilitySpec.GetPrimaryInstance())
			{
				// 对于实例化能力 - 使用新的 API
				PredictionKey = AbilityInstance->GetCurrentActivationInfo().GetActivationPredictionKey();
			}
			InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputReleased, AbilitySpec.Handle, PredictionKey);
		}
	}
}

void UStarrySkyAbilitySystemComponent::ClientEffectApplied_Implementation(UAbilitySystemComponent* AbilitySystemComponent,
                                                     const FGameplayEffectSpec& EffectSpec, FActiveGameplayEffectHandle ActiveEffectHandle) const
{
	//获取资产标签容器,根据效果的资源标签进行飘字显示
	FGameplayTagContainer TagsContainer;
	EffectSpec.GetAllAssetTags(TagsContainer);
	EffectAssetTags.Broadcast(TagsContainer);
}
